#pragma once
#include"Log.hpp"
#include<string>
#include<functional>
#include<strings.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
//#define INADDR_ANY ((int_addr_t)0x00000000)//inet_addr函数的宏定义
using namespace LogModule;
using func_t=std::function<std::string(const std::string&)>;//定义了一个函数类型
const int defaultfd = -1;

//你是为了网络通信的
class UdpServer
{ 
public:
    //UdpServer(const std::string& ip, uint16_t port)//将ip和端口告诉我
    UdpServer(uint16_t port, func_t func)//端口告诉我就行了,func是结果处理函数
    :_sockfd(defaultfd)//初始化为-1
    ,_port(port)
    ,_func(func)
   //,_ip(ip)
    {}
    void Init()//初始化
    {
        //1.创建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd<0)//失败了
        {
            LOG(LogLevel::ERROR)<<"create socket failed!";
            exit(1);
        }
        LOG(LogLevel::INFO)<<"create socket success!,socketfd="<<_sockfd;

        
        //2.绑定socket信息，ip和端口
        //2.1填充socketaddr_in结构体,是处理 IPv4 网络地址的结构体
        struct sockaddr_in local;
        bzero(&local, sizeof(local));//将这个结构体清空
        local.sin_family = AF_INET;//网络通信，填充协议家族
        //我会将我们的IP和端口发给对方
        //IP和端口信息一定要发送给网络
        //本地格式->网络序列
        local.sin_port = htons(_port);//端口号（将主机系列转换为网络序列）
        //IP也是如此，我们要将IP转换为四字节，将IP转换为网络序列，inet_addr一个函数能完成前面的两个功能
        //local.sin_addr.s_addr = inet_addr(_ip.c_str());//将我们的ip地址转换为网络的四字节IP
        local.sin_addr.s_addr = INADDR_ANY;//将本地地址设置为0.0.0.0，表示监听所有地址

        //2.2绑定本地的套接字
        int n=bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
        //这行代码的作用是将一个套接字（由 _sockfd 表示）和一个本地地址（由 local 表示）绑定在一起。
    /*
        (struct sockaddr*)&local：这里进行了类型转换，
        把 local 地址的指针转换为通用的 sockaddr 类型。
        local 一般是 sockaddr_in（针对 IPv4）
        或者 sockaddr_in6（针对 IPv6）类型的结构体变量。
    
    */
        if(n<0)//失败了
        {
            LOG(LogLevel::FATAL)<<"bind socket failed!";
            exit(2);
        }
        LOG(LogLevel::INFO)<<"bind socket success!,sockfd="<<_sockfd;
    }
    void Start()//服务器启动
    {
        //我们要让我们的服务器继续一个死循环的状态
        _isrunning=true;
        while(_isrunning)
        {
            //1.接收数据
            char buffer[1024];//定义提一个缓冲区
    //定义 sockaddr_in 类型的变量 peer，用于存储远端（客户端）的地址信息，
    //包括 IP 地址和端口等（针对 IPv4 网络地址）
            struct sockaddr_in peer;//远端,客户端
            socklen_t len=sizeof(peer);//长度
            ssize_t s=recvfrom(_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&peer, &len);
            if(s>0)//说明我们读到了有效的数据
            {
                //客户端的信息是保存在peer中的，我们可以打印出来看看
                int peer_port=ntohs(peer.sin_port);//从网络中拿到的客户端的端口，属于网络序列，我们需要转换为主机系列
                std::string peer_ip=inet_ntoa(peer.sin_addr);//4字节的网络风格的ip地址，我们要转换为点分十进制字符串风格的形式
                buffer[s]=0;//添加一个结束符
                
                std::string result=_func(buffer);//将消息传递给回调函数，让这个函数进行处理，
                //调用函数对buffer中的数据进行处理，然后将处理结果存储在result中


                LOG(LogLevel::DEBUG)<<"["<<peer_ip<<":"<<peer_port<<"]"<<buffer;//1.消息内容，2.消息是谁发的
                
                //读消息成功了，那么我们就进行一个反馈，发消息操作
                //2.发消息
                // std::string echo_stirng="server say@";
                // echo_stirng+=buffer;
                //sendto(_sockfd, echo_stirng.c_str(), echo_stirng.size(), 0, (struct sockaddr*)&peer, len);

                sendto(_sockfd, result.c_str(), result.size(), 0, (struct sockaddr*)&peer, len);
            }
            
            

        }
    }
    ~UdpServer()
    {}
private:
    int _sockfd;//套接字
    uint16_t _port;//端口号
    //std::string _ip;//ip地址，用的是字符串风格的IP地址，点分十进制
    bool _isrunning;//是否正在运行的状态

    func_t _func;//服务器的回调函数，用来对数据进行处理操作
};